home *** CD-ROM | disk | FTP | other *** search
- /*
- DFilePaths.c
-
- A Dragon plug-in developed using THINK C 5.0
-
- Places a list of the full pathnames of the files dropped on it into the clipboard (desk scrap).
-
- Copyright © 1992 by Paul M. Hoffman
- Send feedback to paul.hoffman@um.cc.umich.edu
-
- This code may be freely used, altered, and distributed in any way you want as long as:
- 1. It is GIVEN away rather than sold;
- 2. This statement and the above copyright notice are left intact.
-
- NOTE: You should set the "Stationery aware" SIZE bit for this — since it doesn't actually
- do anything to the things dropped on it, we don't want the Finder to think it has to
- make a copy of a stationery file before we get to see it!
-
- Created 12 Apr 1992 Typed in ad hoc, so I know it'll be full o' bugs!
- Modified 14 Apr 1992 v0.1 Rewritten in a more modular and well-thought-out fashion
- — only now it's stuck on a (gasp!) "syntax error"
- v0.2 First working version. Tested to 91 files, 9K of pathnames
- (which means that GrowByAndPoint works — gee!)
- v0.3 OOPSied
- 30 Apr 1992 Trying to get a colon at the end of the path for directories
- 01 May 1992 OK, I give up!
- 02 May 1992 So I didn't give up after all — FSpIsFile works OK now (it
- even returns FALSE for volumes)
- Unfortunately, GetFullPath screws up when you give it
- an FSSpec to a volume!
- v0.4 Fixed (I think) — doing PBGetCatInfo on a volume will give
- an ioParID of 1 — no thanks to Apple for their lousy
- documentation of PBGetCatInfo in Inside Macintosh!
- I hope this hack doesn't break!
- 11 May 1992 Wait … looks like an FSpec.parID == fsRtParID (i.e., 1) by
- itself is enough to identify a volume without calling
- PBGetCatInfo. Why didn't I think of that before?
- Began implementing menus (Apple, File, Edit, Options)
- Lots o' bugs!
- 12 May 1992 Quoting options work but choosing 'Text Files Only' crashes
- with a bus error in the RTS at the end of DoOptionsMenu
- 14 May 1992 Lowered expectations — options menu now contains just No
- Quotes, Single Quotes, and Double Quotes
- 17 May 1992 v1.0b1 First release (limited)
- 19 May 1992 v1.1 Raised expectations again — implemented "Full Paths" and
- "Text Files Only" settings (in Options menu) — and brought
- back angled brackets, which only make sense if you can
- get file names (as opposed to full paths). And it all works!
- 20 May 1992 v1.1b1 Second release
-
- Possible enhancements ¶ Make an About box (oops! — forgot)
- ¶ Improve error reporting
- ¶ Implement a settings resource in which options are stored. An
- 'Opts' resource will exist in the application file and a Preferences
- file. If there is no Preferences file, we'll create it. If it couldn't
- be created for some reason, we'll use the 'Opts' resource from
- the application (which won't be modified except maybe by clever
- ResEditors). The first time it's created, we'll stuff a copy of the
- resource from the application into it. Check the resource for
- nonsensical values when the app starts. Checksum? But that
- would prevent ResEdit hacking. Unless a zero checksum
- indicates it should be recalculated …
- ¶ Add an option to create a file with the paths/names instead of
- copying them to the clipboard. Also append to a file?
- ¶ Consider making a FileGroup class. Methods would include an
- iterator (ForEachFile), a qualified iterator (ForEachMatchingFile),
- a sub-setting method (ExtractMatchingFiles), etc.
- ¶ Also consider a File class with Specify (a flexible Specify, unlike
- TCL's fossilized CFile::Specify___ methods), GetName, GetAlias,
- GetPath, GetSpec (again, generalized), etc. methods.
- ¶ Think of a better word for Finder objects (File, Directory, Folder,
- and Volume are too specific — we need a generic word for any 1 of
- these) — and make a class for it.
- */
-
- #include "Dragon.h"
- #include "FileUtils.h"
- #include <Files.h>
- #include <Types.h>
-
- #define cRETURN '\r'
- #define cNIL '\0'
- #define cCOLON ':'
-
- #define szMaxNameLength 32 // FSSpec file names can be up to 64 bytes, but we're
- // going to pretend MFS doesn't exist — and file
- // names under HFS can only be 32 bytes
- #define szMaxPathLen 1000 // I have no idea whether this would ever be surpassed
- #define szExtraRoom 400 // Extra bytes to minimize # of GrowByAndPoint's needed
- #define szPathLenEstimate 70 // Estimated average path length
- #define szGrowBy szPathLenEstimate * 6
-
- static char quotes[4][2] = { { cNIL, cNIL }, { '\'', '\'' }, { '"', '"' }, { '<', '>' } };
-
- enum {
- mApple = 128,
- mFile,
- mEdit,
- mOptions
- };
-
- enum {
- iAbout = 1,
- iLine1
- };
-
- enum {
- iQuit = 1
- };
-
- enum {
- iFullPaths = 1,
- iLine2,
- iNoQuotes,
- iSingleQuotes,
- iDoubleQuotes,
- iAngleBrackets,
- iLine3,
- iTextFilesOnly
- };
-
- short GetFileName (FSSpec *fsspec, char *name);
- long GetFullPath (FSSpec *fsspec, char *path, OSErr *err);
- unsigned short ReverseCopyP2CStr (unsigned char *pas, char *c);
- void ReverseCStr (char *str);
- Boolean FSpIsFile (FSSpec *fss);
- Boolean FSpIsVolume (FSSpec *fss); // Bonus function
- void CheckOne (MenuHandle menu, short first, short last, short itemToCheck);
- Boolean ToggleMenuItem (MenuHandle menu, short item);
- Boolean ItemIsChecked (MenuHandle menu, short item);
-
- class DFilePaths: Dragon {
-
- protected:
- char leftQuote;
- char rightQuote;
- Boolean textFilesOnly;
- Boolean fullPaths;
- MenuHandle appleMenu;
- MenuHandle fileMenu;
- MenuHandle editMenu;
- MenuHandle optionsMenu;
-
- public:
- DFilePaths (void);
- virtual OSErr ProcessDroppings (FSSpec **docs, long numDocs);
-
- protected:
- virtual void SetUpMenus (void);
- virtual void InitOptionsMenu (void);
- virtual void SetQuotesOption (short optionItem);
- virtual void ToggleTextFilesOnly (void);
- virtual void ToggleFullPaths (void);
- virtual void DoMenu (long menuItemCode);
- virtual void DoAppleMenu (short itemNum);
- virtual void DoFileMenu (short itemNum);
- virtual void DoEditMenu (short itemNum);
- virtual void DoOptionsMenu (short itemNum);
- virtual void DoAbout (void);
- };
-
- DFilePaths::DFilePaths (void)
- {
- autoQuit = FALSE;
- textFilesOnly = TRUE;
- fullPaths = TRUE;
- }
-
- void DFilePaths::SetUpMenus (void)
- {
- if (menusInstalled) // Just in case … (see Dragon::SetUpMenus)
- return;
-
- appleMenu = GetMenu (mApple); // Standard Apple Menu
- AddResMenu (appleMenu, 'DRVR');
- InsertMenu (appleMenu, 0);
-
- fileMenu = GetMenu (mFile);
- InsertMenu (fileMenu, 0);
-
- editMenu = GetMenu (mEdit);
- InsertMenu (editMenu, 0);
-
- optionsMenu = GetMenu (mOptions);
- InsertMenu (optionsMenu, 0);
- InitOptionsMenu ();
-
- DrawMenuBar ();
- menusInstalled = TRUE;
- }
-
- void DFilePaths::InitOptionsMenu (void)
- {
- // Initialize options menu (and set the options while we're at it)
- // Start out with no quotes
- SetQuotesOption (iNoQuotes);
- CheckItem (optionsMenu, iTextFilesOnly, textFilesOnly);
- CheckItem (optionsMenu, iFullPaths, fullPaths);
- }
-
- void DFilePaths::SetQuotesOption (short optionItem)
- {
- CheckOne (optionsMenu, iNoQuotes, iAngleBrackets, optionItem);
- leftQuote = quotes [optionItem - iNoQuotes][0];
- rightQuote = quotes [optionItem - iNoQuotes][1];
- }
-
- void DFilePaths::ToggleTextFilesOnly (void)
- {
- textFilesOnly = ToggleMenuItem (optionsMenu, iTextFilesOnly);
- }
-
- void DFilePaths::ToggleFullPaths (void)
- {
- fullPaths = ToggleMenuItem (optionsMenu, iFullPaths);
- }
-
- void DFilePaths::DoMenu (long menuItemCode)
- {
- short menuID, itemNum;
- long ticks;
-
- menuID = menuItemCode >> 16;
- itemNum = menuItemCode & 0xFFFF;
-
- switch (menuID) {
- case mApple:
- DoAppleMenu (itemNum);
- break;
- case mFile:
- DoFileMenu (itemNum);
- break;
- case mEdit:
- DoEditMenu (itemNum);
- break;
- case mOptions:
- DoOptionsMenu (itemNum);
- break;
- default:
- break;
- }
- Delay (2, &ticks); // Wait a little to let the user see the hilited menu title
- HiliteMenu (0);
- }
-
- void DFilePaths::DoAppleMenu (short itemNum)
- {
- Str255 itemStr;
-
- if (itemNum == iAbout)
- DoAbout ();
- else {
- GetItem (appleMenu, itemNum, itemStr);
- OpenDeskAcc (itemStr);
- }
- }
-
- void DFilePaths::DoFileMenu (short itemNum)
- {
- StopRunning ();
- }
-
- void DFilePaths::DoEditMenu (short itemNum)
- {
- // We don't do anything with this menu …
- }
-
- void DFilePaths::DoOptionsMenu (short itemNum)
- {
- switch (itemNum) {
- case iFullPaths:
- ToggleFullPaths ();
- break;
- case iNoQuotes:
- case iSingleQuotes:
- case iDoubleQuotes:
- case iAngleBrackets:
- SetQuotesOption (itemNum);
- break;
- case iTextFilesOnly:
- ToggleTextFilesOnly ();
- break;
- default:
- break;
- }
- }
-
- void DFilePaths::DoAbout (void)
- {
- // Don't do anything yet …
- }
-
- OSErr DFilePaths::ProcessDroppings (FSSpec **docs, long numDocs)
- {
- char *threshold, *limit;
- register char *p;
- OSErr err = noErr;
- long blockLen, len = 0L, pathsLen = 0L, i;
- Handle pathsHndl;
- FSSpec *fss;
-
- // First, allocate a block big enough to contain a reasonable number of paths
- // — this may be in the application zone or in the "temporary memory" space
- blockLen = szPathLenEstimate * numDocs + szMaxPathLen + szExtraRoom;
- pathsHndl = (Handle) AnyHandle (blockLen);
- if (pathsHndl == NULL)
- return errAEEventNotHandled;
-
- // Now set up three pointers —
- // p is a scan pointer that begins by pointing at the first byte in the block
- // threshold points to the byte szMaxPathLen bytes from the end of the block
- // limit points to the last byte in the block
-
- p = *pathsHndl;
- limit = p + blockLen - 1;
- threshold = limit - szMaxPathLen;
-
- HLock ((Handle) docs);
- HLock ((Handle) pathsHndl);
-
- // Now, go through the array of FSSpecs getting the name or full path for each
- for (fss = *docs; numDocs--; fss++) {
-
- // Don't process non-text files unless "Text Files Only" is unchecked
- err = noErr;
- if (textFilesOnly == FALSE || FSpType (fss, &err) == 'TEXT') {
- if (err != noErr) // We may have gotten an error in FSpType
- continue; // — just go on to the next one if we did
-
- // Stick a left quote in front, if appropriate
- if (leftQuote) {
- *p++ = leftQuote;
- pathsLen++;
- }
-
- // Now copy the file name or full path, depending on the setting of the "Full Paths" option
- if (fullPaths) {
- len = GetFullPath (fss, p, &err);
- if (err != noErr) // Just skip this one if we had any problems
- continue;
- } else
- len = (long) GetFileName (fss, p);
- p += len;
- pathsLen += len;
-
- // Stick a right quote at the end, if appropriate
- if (rightQuote) {
- *p++ = rightQuote;
- pathsLen++;
- }
-
- // Add a return character and check to see if we're running out of room
- // in the block we allocated to hold the list of file names/paths
- *p++ = cRETURN;
- pathsLen++;
- if (p > threshold) {
-
- // Check to see if we've thrashed what lies beyond the allocated block
- if (p > limit) {
- err = errAEEventNotHandled;
- break;
- }
-
- // Grow the block and keep our place in it — this unlocks the block,
- // resizes it, relocks the block, and returns a pointer to the same
- // byte in the block where we were before
- p = GrowByAndPoint (pathsHndl, p, szGrowBy, &blockLen);
-
- // Reset limit and threshold to point to the appropriate places
- threshold = (limit = p + blockLen - 1) - szMaxPathLen;
- }
- }
- }
- *p = cNIL;
- HUnlock ((Handle) docs);
- DisposHandle ((Handle) docs);
-
- SetHandleSize (pathsHndl, pathsLen);
- if (err == noErr && *pathsHndl != cNIL) // Don't change the clipboard if there was an error
- err = HandleToScrap (pathsHndl, 'TEXT'); // or if no droppings were processed
- HUnlock (pathsHndl);
- DisposHandle (pathsHndl);
-
- return err;
- }
-
- Dragon *CreateGDragon (void)
- {
- return (Dragon *) new DFilePaths;
- }
-
- short GetFileName (register FSSpec *fsspec, register char *name)
- {
- short len, count;
- unsigned char *p, *limit;
-
- p = (unsigned char *) fsspec->name;
- limit = p + len;
- count = len = *p++;
- while (count-- != 0)
- *name++ = *p++;
- return len;
- }
-
- long GetFullPath (FSSpec *fsspec, char *path, OSErr *err)
- {
- short volume = fsspec->vRefNum;
- OSErr fsErr = noErr;
- CInfoPBRec catinfo;
- char dirName[64];
- long retLen = 0L, len;
- register char *p = path;
-
- // All comments assume a file whose full path is
- // HD:Fonts:Garamond
-
- // If fsspec doesn't designate a file (i.e., it's a volume or directory), we write a colon
- if ( ! FSpIsFile (fsspec)) {
- *p++ = cCOLON;
- retLen++;
- }
-
- // First, copy the file name backwards from a Pascal to a C string:
- // "\pGaramond" becomes "dnomaraG"
-
- len = (long) ReverseCopyP2CStr (fsspec->name, p);
- p += len;
- retLen += len;
-
- if ( ! FSpIsVolume (fsspec)) { // Don't do anything more if we've got a volume
-
- catinfo.dirInfo.ioVRefNum = volume;
- catinfo.dirInfo.ioNamePtr = (StringPtr) dirName;
- catinfo.dirInfo.ioDrParID = fsspec->parID;
-
- // Now copy the rest of the path, one level at a time, backwards:
- // "dnomaraG:stnoF:DH"
- do {
- catinfo.dirInfo.ioFDirIndex = -1;
- catinfo.dirInfo.ioDrDirID = catinfo.dirInfo.ioDrParID; // <= This is the key — go from the
- // current folder to its parent
- fsErr = PBGetCatInfoSync (&catinfo);
- if (fsErr == noErr) {
- *p++ = cCOLON;
- retLen += 1 + (len = ReverseCopyP2CStr ((StringPtr) dirName, p));
- p += len;
- } else {
- *err = fsErr;
- return retLen;
- }
- } while (catinfo.dirInfo.ioDrDirID != fsRtDirID);
- }
-
- // Finally, reverse the string and return its length and any error that might have occurred
- ReverseCStr (path);
- *err = fsErr;
- return retLen;
- }
-
- unsigned short ReverseCopyP2CStr (register unsigned char *pas, register char *c)
- {
- register short i, len = *pas++;
-
- for (i = len, c += len; i > 0; i--)
- *--c = *pas++;
- c += len;
- *c = cNIL;
- return len;
- }
-
- void ReverseCStr (register char *str)
- {
- register short n = 0L, i;
- register char t, *p1, *p2;
-
- p1 = p2 = str;
- while (*p2++)
- ;
- p2--;
- n = (p2 - p1) / 2;
- for (i = n; i > 0; i--) {
- t = *--p2;
- *p2 = *p1;
- *p1++ = t;
- }
- }
-
- Boolean FSpIsFile (FSSpec *fss)
- {
- CInfoPBRec catinfo;
- OSErr err;
-
- if (FSpIsVolume (fss))
- return FALSE;
- catinfo.hFileInfo.ioNamePtr = fss->name;
- catinfo.hFileInfo.ioVRefNum = fss->vRefNum;
- catinfo.hFileInfo.ioFDirIndex = 0;
- catinfo.hFileInfo.ioDirID = fss->parID;
- err = PBGetCatInfoSync (&catinfo);
- return ! (catinfo.hFileInfo.ioFlAttrib & ioDirMask); // Bit 4 indicates it's a folder (or volume)
- }
-
- Boolean FSpIsVolume (FSSpec *fss)
- {
- return (fss->parID == fsRtParID); // I guess this is what this constant is for …
- }
-
- void CheckOne (MenuHandle menu, register short first, register short last, register short itemToCheck)
- {
- register short i;
- short mark;
-
- if (itemToCheck < first || itemToCheck > last)
- return;
- for (i = first; i < itemToCheck; i++)
- SetItemMark (menu, i, noMark);
- SetItemMark (menu, itemToCheck, checkMark);
- for (i = itemToCheck + 1; i <= last; i++)
- SetItemMark (menu, i, noMark);
- }
-
- Boolean ItemIsChecked (MenuHandle menu, short item)
- {
- short mark;
-
- GetItemMark (menu, item, &mark);
- return (mark == checkMark);
- }
-
- Boolean ToggleMenuItem (MenuHandle menu, short item)
- {
- Boolean newSetting;
-
- newSetting = ! ItemIsChecked (menu, item);
- CheckItem (menu, item, newSetting);
- return newSetting;
- }
-